package model;

import java.awt.geom.Point2D;
import java.awt.geom.RectangularShape;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Stack;

import model.Player.PlayerState;
import ctf.model.Side;
import ctf.model.TeamColor;
import ctf.view.GameViewInterface.PlayerInfo;

/**
 * Class used to manage the organization of teams.
 * @author N. Bhatia, A. Butt, N. Evans, C. Levesque, P. OKeefe
 * A class to hold a teams players, flag, color, score and jail.
 *
 */
public class Team implements IPublishable{
	private final Flag flag;
	private final Player[] players;
	private final Stack<Player> jailedPlayers;
	private int score;
	private final RectangularShape jail;
	private TeamColor color;
	private final Side side;
	private double currentExclusionZoneRadius ;
	
	public Team(Side side) {
		this.flag = new Flag();
		this.players = new Player[GameConstants.NUM_PLAYERS_PER_SIDE];
		this.jailedPlayers = new Stack<Player>();
		this.score = 0;
		this.side = side;
		this.currentExclusionZoneRadius = GameConstants.MAXIMUM_EXCLUSION_ZONE_RADIUS;
		if (side == Side.LEFT) {
			this.jail = GameConstants.LEFT_TEAM_JAIL;
		} else { //side==Side.Right
			this.jail = GameConstants.RIGHT_TEAM_JAIL;
		}
		
	}
	/**
	 * Places players in their standard starting positions.
	 * @param playerStartingPositions An array of starting positions.
	 * @param defaultPlayerOrientation The default orientation for players.
	 */

	public void initializePlayerPositions(Point2D[] playerStartingPositions,
			double defaultPlayerOrientation) {
		for (int i = 0; i < playerStartingPositions.length; i++) {
			players[i] = new Player();
			players[i].setPosition((Point2D)playerStartingPositions[i].clone());
			players[i].setOrientation(defaultPlayerOrientation);
			players[i].setSide(side);
		}
	}
	
	/**
	 * Returns a collection containing PlayerInfo for each player on the team
	 * @return
	 */
	public Collection<PlayerInfo> getPlayerInfo() {
		Collection<PlayerInfo> playerInfos = new ArrayList<PlayerInfo>();
		for (Player player : players) {
			playerInfos.add(player.getInfo());
		}
		return playerInfos;

	}

	/**
	 * Returns the flag object for the team
	 * @return
	 */
	public Flag getFlag() {
		return this.flag;
	}

	/**
	 * Finds a specific player and returns it
	 * @param playerNumber - The player to find
	 * @return Player - the player to return
	 */
	public Player getPlayer(int playerNumber) {
		return players[playerNumber];
	}
	
	/**
	 * Returns the teams score
	 * @return Team score
	 */
	public int getScore() {
		return this.score;
	}

	
	/**
	 * Adds points to the teams score
	 * @param points - The number of points to add
	 */
	public void addToScore(int points) {
		this.score += points;
	}
	
	/**
	 * Get the teams jail
	 * @return The teams jail
	 */
	public RectangularShape getJail() {
		return this.jail;
	}

	/**
	 * Set the teams color
	 * @param color the teams color
	 */
	public void setColor(TeamColor color) {
		this.color = color;
	}

	/**
	 * Get the teams color
	 * @return The teams color
	 */
	public TeamColor getColor() {
		return this.color;
	}

	/**
	 * The players contained in this team.
	 * @return An array of players on this team.
	 */
	public Player[] getPlayers() {
		return players;
	}
	
	/**
	 * Enables the jailing of players
	 * @param player The player to jail.
	 * @param jail The jail to place the player in.
	 * @return true if player was added to the jail, false if not
	 */
	public boolean jailPlayer(Player player, RectangularShape jail) {		
		if (containsPlayer(player) && !jailedPlayers.contains(player)) {
			player.setPosition(jail.getCenterX(), jail.getCenterY());
			player.setState(PlayerState.PRISONER);
			jailedPlayers.push(player);
			return true;
		} 
		else {
			return false;
		}
	}
	
	/**
	 * Releases a player placed in the jail.
	 */
	public void releasePlayer() {
		if (!jailedPlayers.isEmpty()) {
			jailedPlayers.pop().setState(PlayerState.ESCAPEE);
		}
	}
	
	/**
	 * Determines if an entire team has been jailed at once.
	 * @return True if all players are jailed.
	 */
	public boolean isEntireTeamJailed() {
		if (jailedPlayers.size() == players.length) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Resets the team to defaults.
	 */
	public void reset() {
		this.flag.reset();
		for (int i = 0; i < players.length; i++) {
			GameEntityManager.Instance().unregisterGameEntity(players[i]);
		}
		jailedPlayers.clear();
		this.score = 0;
	}
	
	/**
	 * Sets the radius of the exclusion zone for use in TCTF calculations.
	 * @param radius The new radius of TCTF
	 */
	public void setFlagExclusionRadius(double radius){
		this.currentExclusionZoneRadius  = radius;
	}
	
	/**
	 * Returns the radius of the Flag Exclusion Zone
	 * @return TCTF radius
	 */
	public double getFlagExclusionRadius(){
		return currentExclusionZoneRadius ;
	}
	

	@Override
	public String publish(Side side){
		String teamString = new String();
		teamString = " ";
		teamString += getColor().name().toLowerCase() + " ";
		teamString += getScore();
		teamString += getFlag().publish(side);
		for (int i = 0; i < players.length; i++) {
			teamString += players[i].publish(side);
		}
		return teamString;
	}
	
	/**
	 * Returns the side of the team.
	 * @return teams side.
	 */
	public Side getSide() {
		return side;
	}
	
	public boolean containsPlayer(Player player) {
		for (int i = 0; i < players.length; i++) {
			if (players[i] == player) {
				return true;
			}
		}
		return false;
	}
}
